#!/usr/bin/env python
# -*- coding:utf-8 -*-

'''
# Author:linjinting for 02010166
@file: tcp_server.py
@time: 2020/3/14 13:50
'''
import selectors
import socket
from unit import find_caller


class TCPServer(object):

    def __init__(self, *args, **kwargs):
        self.m_local_address = None
        self.m_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.m_client_vector = dict()
        self.client_count = 0
        self.read_tread_list = list()
        self.m_reader_count = 0
        if args:
            if not isinstance(args[0], tuple):
                raise TypeError("args must be tuple")
            self.m_local_address = args[0]
        # print(self.socket)

        if not self.m_local_address:
            return
        self.callback = kwargs.get('callback')
        self.m_channel = kwargs.get('channel')

        _timeout = kwargs.get('timeout', None)
        _blocking = kwargs.get('blocking', False)
        self._max_listen = kwargs.get('max_listen', 1024)
        if _timeout:
            self.m_socket.settimeout(_timeout)  # 设置 timeout
        self.m_socket.setblocking(_blocking)  # 设置 blocking ，默认非阻塞
        # self.m_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 加入socket配置，重用ip和端口
        self.m_selector = selectors.DefaultSelector()  # 生select实例对象

    def start(self):
        try:
            self.m_socket.bind(self.m_local_address)
            self.m_socket.listen(self._max_listen)
            # 打印---
            self.print_and_logger(msg='[server] listening\n', name='listen', type='event', level='info',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)
            """
            # 参数一：sock 进行监听
            # 参数二：selectors.EVENT_READ 执行动作
            # 参数三：accept,只要来一个链接就回调这个函数
            """
            self.m_selector.register(self.m_socket, selectors.EVENT_READ, self.accept)
            self.monitor()

        except OSError as ret:
            # if ret.errno == 10035:
            self.print_and_logger(msg='%s\n' % ret.__str__(), name='sclose', type='event', level='error',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)

    def monitor(self):
        """监听EVENT事件列表,有活动的事件就交给相应方法处理"""
        while True:
            try:
                # 调用select：优先使用epoll
                events = self.m_selector.select()
                # 只要不阻塞就有调用的数据，返回一个列表
                # 默认阻塞，有活动链接就返回活动的链接列表
                for key, mask in events:
                    # callback相当于调accept函数
                    callback = key.data
                    # 获取函数内存地址，加入参数
                    # key.fileobj = 文件句柄
                    # print(mask)
                    callback(key.fileobj)
            except OSError as ret:
                # print(ret)
                break

    def accept(self, sock):

        # 接收链接
        # if self.client_count == 400:
        #     print('client m_reader_count too many')
        #     return
        conn, addr = sock.accept()  # Should be ready
        self.m_client_vector[str(addr)] = conn  # 存储客户端连接socket {addr:conn}
        self.print_and_logger(msg='[server] There are client connecting in\n', name='connect', type='event',
                              level='info',
                              channel=self.m_channel, caller=find_caller(),
                              laddr=self.m_local_address, raddr=conn.getpeername())

        # 链接设置非阻塞模式
        conn.setblocking(False)
        # 注册conn，回调 read函数
        self.m_selector.register(conn, selectors.EVENT_READ, self.read)
        # print(self.m_selector._readers)

    def read(self, conn):
        try:
            data = conn.recv(1024)  # Should be ready

        except WindowsError as ret:
            print(ret)
            self.close_client(conn)
        else:
            if data:
                # print('recv:', data.hex())
                self.m_reader_count = self.m_reader_count + len(data)

                self.print_and_logger(msg='[server] recv data\n', data=data, name='read', type='event', level='info',
                                      side='recv', channel=self.m_channel, client=conn, server=self,
                                      caller=find_caller(), laddr=self.m_local_address, raddr=conn.getpeername())
            else:
                self.print_and_logger(msg='[server] remote client closing\n', name='cclose', type='event', level='info',
                                      side='recv', channel=self.m_channel, caller=find_caller(),
                                      laddr=self.m_local_address, raddr=conn.getpeername())
                # 取消注册
                self.m_selector.unregister(conn)

                self.close_client(conn)

    def get_recv_count(self):
        _count = self.m_reader_count
        self.m_reader_count = 0
        return _count

    def send_msg_to_client(self, conn, data, isReply=False):
        try:
            # 一般用sendall
            conn.sendall(data)
            if isReply:
                msg = '[client][reply] send msg success\n'
            else:
                msg = '[client] send msg success\n'
            self.print_and_logger(msg=msg, data=data, type='logger', level='info',
                                  side='send', channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=conn.getpeername())
        except OSError as ret:
            self.print_and_logger(msg='%s\n' % ret.__str__(), name='send', type='event', level='error',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)

    def send_msg(self, data='recv'):
        if len(self.m_client_vector) == 0:
            self.print_and_logger(msg='[server] not found connecting clients\n', type='logger', level='info',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)
            return

        for _, conn in self.m_client_vector.items():
            self.send_msg_to_client(conn, data)

    def get_client(self, addr):
        """
        # 获取客户端连接 文件句柄
        :param addr: str  (host, port)
        :return: # 返回client 文件句柄
        """
        return self.m_client_vector[addr]

    def get_clients(self):
        return self.m_client_vector

    def close_client(self, conn, raddr=None):
        # print(self.m_selector._readers)
        # self.m_selector.unregister(conn)  # 取消注册

        try:
            self.m_client_vector.pop(str(conn.getpeername()))
            conn.close()  # 关闭链接
            # msg = 'close client'
            self.print_and_logger(msg='[server] close client\n', name='cclose', type='event', level='info',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=raddr)
        except OSError as ret:
            self.print_and_logger(msg='%s\n' % ret.__str__(), name='cclose', type='event', level='error',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address, raddr=raddr)

    def close(self):
        if self.m_socket is None:
            return
        for _, client in self.m_client_vector.items():
            self.close_client(client)
        try:
            self.m_socket.close()
            self.m_socket = None
            # msg = 'server close listen'
            self.print_and_logger(msg='[server] close listen\n', name='sclose', type='event', level='info',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)

        except Exception as ret:
            self.print_and_logger(msg='%s\n' % ret.__str__(), name='sclose', type='event', level='error',
                                  channel=self.m_channel, caller=find_caller(),
                                  laddr=self.m_local_address)

    def set_channel(self, channel):
        self.m_channel = channel

    def print_and_logger(self, **kwargs):
        if self.callback:
            self.callback(**kwargs)
        else:
            print(kwargs)


if __name__ == '__main__':
    import sys

    host = '192.168.11.111'
    port = 5555
    address = (host, port)
    tcps = TCPServer(address)
    tcps.start()
    tcps.monitor()
    while True:
        line = sys.stdin.readline()
        print(line)
        if line == '\n':
            tcps.close()
            break
